package org.nvframe.component.physics.state;

import org.jbox2d.collision.MassData;
import org.jbox2d.collision.shapes.CircleDef;
import org.jbox2d.collision.shapes.PolygonDef;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.collision.shapes.ShapeDef;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.World;

import org.newdawn.slick.geom.Vector2f;
import org.nvframe.component.state.AbstractPosition;
import org.nvframe.entity.Entity;
import org.nvframe.manager.RegisterManager;
import org.nvframe.util.NVMathUtils;
import org.nvframe.util.settings.SettingsObj;

/**
 * 
 * @author Nik Van Looy
 */
public class PhysicsPosition extends AbstractPosition {

	private World world;
	private Body body = null;
	private Shape shape = null;
	private boolean bullet;
	private boolean sensor;
	private boolean staticBody;
	private boolean directionFromPhys;
	
	public PhysicsPosition(String id, Entity owner, SettingsObj settings) {
		super(id, owner);

		scale = 1;
		direction = 0;

		// directions from NVMathUtils or manual override?
		directionFromPhys = settings.getBoolean("directionFromPhys", true);
		
		world = (World) RegisterManager.getInstance().getOption("world");
		
		// NVMathUtils settings from entitysettings
		String physShape = settings.getString("phys_shape", "Box");
		float mass = settings.getString("phys_mass", "INFINITE_MASS").equals("INFINITE_MASS") ? NVMathUtils.INFINITE_MASS : 
			Float.parseFloat(settings.getString("phys_mass"));
		
		staticBody = settings.getBoolean("phys_static", false);
		bullet = settings.getBoolean("phys_bullet", false);
		
		// create body
    	BodyDef bodyDef = new BodyDef();
    	body = world.createBody(bodyDef);
    	
    	ShapeDef shapeDef;
    	
    	// create shape
    	if(physShape.equals("Circle")) {
	    	shapeDef = new CircleDef();
			
			// height en width
			width = settings.getFloat("width",  settings.getFloat("radius", 20.0f));
			height = settings.getFloat("height", settings.getFloat("radius", 20.0f));

	    	((CircleDef)shapeDef).radius = height / NVMathUtils.PIXELS_PER_METER;
    	}
    	else {
	    	// polygon box
	    	shapeDef = new PolygonDef();
			
			// height en width
			width = settings.getFloat("width",  settings.getFloat("width", 20.0f));
			height = settings.getFloat("height", settings.getFloat("height", 20.0f));
			
	    	((PolygonDef)shapeDef).setAsBox(width / NVMathUtils.PIXELS_PER_METER / 2, 
	    			height / NVMathUtils.PIXELS_PER_METER / 2);
		}

    	// damping: used to reduce velocity, value between 0 and 0.1
    	body.setLinearDamping(settings.getFloat("phys_lineardamping", .01f));
    	body.setAngularDamping(settings.getFloat("phys_angulardamping", .01f));

    	// friction: used to reduce velocity while colliding with another body
    	shapeDef.friction = settings.getFloat("phys_friction", 0.1f);
    	
    	// 
    	shapeDef.restitution = settings.getFloat("phys_restitution", 0.1f);

		// default a Box2d body has 0 mass, so therefore it's static by default
    	if(!staticBody) {
	    	MassData md = new MassData();
	    	md.mass = mass;
	    	body.setMass(md);
	    	
	    	// mass: will be automatically calculated by the Box2d engine when not set
	    	shapeDef.density = settings.getFloat("phys_density", 1.0f);
    	}
    	else
    		body.m_type = Body.e_staticType;
    	
    	// sensor only? => not solid!
    	sensor = settings.getBoolean("phys_sensor", false);
		if(sensor)
			shapeDef.isSensor = true;
		
    	// assign the shape defenition to the body
		shape = body.createShape(shapeDef);
    	
    	// is the body a bullet?
    	body.setBullet(bullet);
    	
    	body.applyForce(new Vec2(2, 2), new Vec2(0, 0));
		body.setUserData(owner);
	}

	public void adjustVelocity(float x, float y) {
		Vec2 vel = body.getLinearVelocity();
		vel.x = x;
		vel.y = y;
		body.setLinearVelocity(vel);
	}

	public void applyForce(float x, float y) {
		body.applyForce(new Vec2(x, y), new Vec2(0, 0));
	}
	
	public void applyImpulse(float x, float y) {
		body.applyImpulse(new Vec2(x, y), new Vec2(0, 0));
	}
	
	public Vector2f getLinearVelocity() {
		return new Vector2f(body.getLinearVelocity().x, body.getLinearVelocity().y);
	}
	
	public Vector2f getXY() {
		return new Vector2f(body.getPosition().x * NVMathUtils.PIXELS_PER_METER, 
				body.getPosition().y * NVMathUtils.PIXELS_PER_METER);
	}

	public void setXY(Vector2f position) {
		body.setXForm(new Vec2(position.x / NVMathUtils.PIXELS_PER_METER, 
				position.y / NVMathUtils.PIXELS_PER_METER), body.getAngle());
	}

	public float getDirection() {
		if(directionFromPhys)
			return (float) Math.toDegrees(body.getAngle());
		else
			return direction;
	}

	public void setDirection(float direction) {
		if(directionFromPhys)
			body.setXForm(body.getPosition(), (float) Math.toRadians(direction));
		else
			this.direction = direction;
	}
	
	public Body getBody() {
		return body;
	}
	
	public boolean isBullet() {
		return bullet;
	}

	public void setBullet(boolean bullet) {
		this.bullet = bullet;
	}

	public boolean isSensor() {
		return sensor;
	}

	public void setSensor(boolean sensor) {
		this.sensor = sensor;
	}

	public boolean isStaticBody() {
		return staticBody;
	}

	public void setStaticBody(boolean staticBody) {
		this.staticBody = staticBody;
	}
	
	public Shape getShape() {
		return shape;
	}
	
}

